from google.appengine.ext import db
from google.appengine.api import memcache
from django.utils import simplejson
import logging
class CustomUser(db.Model):
	class Meta:
		db_table = 'CustomUser'
	isSocial = db.BooleanProperty(default = False)
	viewerID = db.StringProperty(default = '')
	consumerKey = db.StringProperty(default = '')
	email = db.StringProperty(default = '')
	name = db.StringProperty(default = '')
	googleAccount = db.UserProperty()
	gfcUserID = db.StringProperty(default = '')
	def getEmail(self):
		if (self.email) and (self.email != ''):
			return self.email
		elif (self.googleAccount) and (self.googleAccount != ''):
			return self.googleAccount.email()
	def getName(self):
		if (self.name) and (self.name != ''):
			return self.name
		elif (self.googleAccount) and (self.googleAccount != ''):
			if (self.googleAccount.nickname() == self.googleAccount.email()):
				return ""
			return self.googleAccount.nickname()

class StudySet(db.Model):
	class Meta:
		db_table = 'StudySet'

	author = db.ReferenceProperty(CustomUser)
	title = db.StringProperty()
	visibility = db.StringProperty()
	date = db.DateTimeProperty(auto_now_add=True)
	studyMaterials = db.TextProperty()
	description = db.StringProperty()
	entryCount = db.IntegerProperty()
	subscriptionCount = db.IntegerProperty();
	timestamp = db.DateTimeProperty(auto_now=True)
	shortUrl = db.TextProperty()

	def tags_dict(self):
		result = {}
		tags = Tag.gql("WHERE set = :set", set = self)
		for t in tags:
			result[t.tag] = True;
		return result
	def tags(self):
		ck = str(self.key()) + '_tags'
		tags = memcache.get(ck)
		if tags is not None:
			return simplejson.loads(tags)
		result = []
		tags = Tag.gql("WHERE set = :set ORDER BY tag ASC", set = self)
		for t in tags:
			if (t.tag != '+') and (t.tag != '-'):
				result.append(t.tag)
		memcache.set(ck,simplejson.dumps(result))
		return result
	def deleteCache(self,all):
		if (not all):
			memcache.delete(str(self.key()))
			return;
		tags = self.tags()
		tags.append('_tags')
		tags.append('')
		memcache.delete_multi(tags, key_prefix=str(self.key()))

	def isOwner(self,user):
		if (self.author.key() == user.key()):
			return True
		if (self.author == user):
			return True

	def isContributor(self,user):
		if (self.isOwner(user)):
			return True
		results = Contributor.gql("WHERE set = :set AND user = :user", set = self, user = user).get()
		if (results):
			return True
		return False

	def removeContributors(self,user,contributors):
		if (self.isContributor(user)):
			for c in contributors:
				if (c == user):
					continue
				r = Contributor.gql("WHERE set = :set AND user = :user", set = self, user = c).fetch(1000)
				db.delete(r)

	def addContributors(self,user,contributors):
		if (self.isContributor(user)):
			for c in contributors:
				exist = Contributor.gql("WHERE set = :set AND user = :user", set = self, user = c).get()
				if (exist is None):
					r = Contributor(set = self, user = c)
					r.put()

	def getContributors(self):
		contributors = []
		results = Contributor.gql("WHERE set = :set", set = self);
		for r in results:
			contributors.append(r.user)
			contributors.append(self.author)
		return contributors

	def getContributorEmails(self):
		emails = []
		contributors = self.getContributors()
		for c in contributors:
			email = c.getEmail()
			if ((email is not None) and (email != '')):
				emails.append(email)
		if (len(emails)):
			return emails

	def isEditable(self,user):
		return self.isContributor(user)

	def isViewable(self,user):
		if (self.isOwner(user)):
			return True
		elif (visibility == 'public'):
			return True
		elif (visibility == 'private'):
			return False
		elif (visibility == 'invited'):
			# @todo Need logic to know if this user was invited to study / contribute to this set
			return False
		return False

	def subscribe(self,user):
		subscription = SetSubscription.gql("WHERE user = :user and set = :set", user = user, set = self).get()
		if (not subscription):
			subscription = SetSubscription(user = user, set = self)
			subscription.put()
		currentSubscription = SetCurrentSubscription.gql("WHERE user = :user and set = :set", user = user, set = self).get();
		if (not currentSubscription):
			currentSubscription = SetCurrentSubscription(user = user, set = self, userDisplayName = user.getName(), userEmail = user.getEmail())
			currentSubscription.put()
			self.subscriptionCount = self.subscriptionCount + 1
			self.put()
			self.deleteCache(all = False)
		# add this deck to all existing locations for current user
		locations = Location.gql("WHERE user = :user ORDER BY location_name", user = user).fetch(10)
		location_decks = []
		for loc in locations:
			location_decks.append(LocationDeck(location = loc, deck = self))
		db.put(location_decks)

	def unsubscribe(self,user):
		currentSubscription = SetCurrentSubscription.gql("WHERE user = :user and set = :set", user = user, set = self).get();
		if (currentSubscription):
			currentSubscription.delete()
			self.subscriptionCount = self.subscriptionCount - 1
			self.put()
			self.deleteCache(all = False)

			preferences = Preferences.gql("WHERE user = :user", user = user).get()
			preferences.current_set = None
			preferences.put()

			# reset history
			study_results = StudyResult.all().filter('user = ',user).filter('set =', self).fetch(100)
			while study_results:
				x = study_results[-1].key()
				db.delete(study_results)
				study_results = StudyResult.all().filter('user = ',user).filter('set =', self).filter('__key__ >', x).fetch(100)

			# remove this deck from all existing locations for current user
			locations = Location.gql("WHERE user = :user ORDER BY location_name", user = user).fetch(10)
			logging.info("About to remove " + self.title + " from existing locations")
			logging.info(user)
			logging.info(locations)
			location_decks = []
			for loc in locations:
				logging.info("Deleting from " + loc.location_name)
				ld = LocationDeck.gql("WHERE location = :location AND deck = :deck", location = loc, deck = self).get()
				if (ld):
					logging.info("Deleting from " + loc.location_name + ". CHECK A")
					location_decks.append(ld)
			db.delete(location_decks);
			# reset the study history
			self.reset_study_result(user)

	def reset_study_result(self,user):
		study_results = StudyResult.all().filter('user = ', user).filter('set = ', self).fetch(100)
		while study_results:
			x = study_results[-1].key()
			db.delete(study_results)
			study_results = StudyResult.all().filter('user = ', user).filter('set = ', self).filter('__key__ >', x).fetch(100)

	def library_delete_not_authored_deck(self,user):
		self.unsubscribe(user)
		subscription = SetSubscription.gql("WHERE user = :user and set = :set", user = user, set = self).get()
		subscription.delete()

	def library_delete_authored_deck(self,user):
		# Deleting a deck is more than deleting it from SetCurrentSubscription and SetSubscription for the current user.
		# In simple case, the user just need to delete deck that is no longer relevant for his need (a private deck).
		# In other case, maybe the user want to stop other users who are currently studying this deck.
		# There are more than one way to delete data (If the user really want to delete the deck, he can delete all the
		# entries)
		# For now, we will delete the deck from SetCurrentSubscription and SetSubscription for the current user.
		# We will also delete the deck from SetCurrentSubscription and SetSubscription for other users who are studying this deck.
		# This function is not yet complete
		self.unsubscribe(user)
		subscription = SetSubscription.gql("WHERE user = :user and set = :set", user = user, set = self).get()
		if (subscription):
			subscription.delete()

		# delete current subscriptions for other users
		current_subscriptions = SetCurrentSubscription.all().filter('set =', self).fetch(100)
		while current_subscriptions:
			x = current_subscriptions[-1].key()
			db.delete(current_subscriptions)
			current_subscriptions = SetCurrentSubscription.all().filter('set =', self).filter('__key__ >', x).fetch(100)

		subscriptions = SetSubscription.all().filter('set =', self).fetch(100)
		while subscriptions:
			x = subscriptions[-1].key()
			db.delete(subscriptions)
			subscriptions = SetSubscription.all().filter('set =', self).filter('__key__ >', x).fetch(100)

		tags = Tag.gql("WHERE set = :set ORDER BY tag ASC", set = self).fetch(100)
		if (tags):
			tags.delete()

		contributors = Contributor.gql("WHERE set = :set", set = self).fetch(1000)
		if (contributors):
			contributors.delete()

		location_decks = LocationDeck.all().filter('deck =', self).fetch(100)
		while location_decks:
			x = location_decks[-1].key()
			db.delete(location_decks)
			location_decks = LocationDeck.all().filter('deck =', self).filter('__key__ >', x).fetch(100)

		self.deleteCache(all = True)
		self.delete()

	def makeThisSetCurrentForUser(self,user):
		preferences = Preferences.gql("WHERE user = :user", user = user).get()
		preferences.current_set = self
		preferences.put()
		return preferences

class Contributor(db.Model):
	class Meta:
		db_table = 'Contributor'
	set = db.ReferenceProperty(StudySet)
	user = db.ReferenceProperty(CustomUser)
	timestamp = db.DateTimeProperty(auto_now=True)

class SetEntry(db.Model):
	class Meta:
		db_table = 'SetEntry'
	set = db.ReferenceProperty(StudySet, collection_name = 'entries')
	question = db.TextProperty()
	answer = db.TextProperty()
	tags = db.StringListProperty()
	timestamp = db.DateTimeProperty(auto_now=True)
	whenAdded = db.DateTimeProperty(auto_now_add=True)
	isPluginApplicable = db.BooleanProperty(default = True)
	isQuizOrTestApplicable = db.BooleanProperty(default = True)

class SetSubscription(db.Model):
	class Meta:
		db_table = 'SetSubscription'
	user = db.ReferenceProperty(CustomUser)
	set = db.ReferenceProperty(StudySet)
	date_subscribed = db.DateTimeProperty(auto_now_add = True)					
	timestamp = db.DateTimeProperty(auto_now=True)

class SetCurrentSubscription(db.Model):
	class Meta:
		db_table = 'SetCurrentSubscription'
	user = db.ReferenceProperty(CustomUser)
	set = db.ReferenceProperty(StudySet)
	timestamp = db.DateTimeProperty(auto_now=True)
	userDisplayName = db.StringProperty(default = '')
	userEmail = db.StringProperty(default = '')
	objective_calendarDays = db.IntegerProperty(default = 0)
	objective_set_on = db.DateProperty()

class StudyResult(db.Model):
	class Meta:
		db_table = 'StudyResult'
	user = db.ReferenceProperty(CustomUser)
	set = db.ReferenceProperty(StudySet)
	entry = db.ReferenceProperty(SetEntry)
	lastDisplayedTime = db.DateTimeProperty(auto_now=True)
	firstDisplayedTime = db.DateProperty(auto_now_add=True)
	shown = db.IntegerProperty()
	wrong = db.IntegerProperty()
	ratio = db.FloatProperty()

class Tag(db.Model):
	class Meta:
		db_table = 'Tag'
	set = db.ReferenceProperty(StudySet)
	tag = db.StringProperty()
	timestamp = db.DateTimeProperty(auto_now=True)

class Location(db.Model):
	class Meta:
		db_table = 'Location'
	user = db.ReferenceProperty(CustomUser)
	location_name = db.StringProperty()

class LocationDeck(db.Model):
	class Meta:
		db_table = 'LocationDeck'
	location = db.ReferenceProperty(Location)
	deck = db.ReferenceProperty(StudySet)

class Preferences(db.Model):
	class Meta:
		db_table = 'Preferences'
	user = db.ReferenceProperty(CustomUser)
	current_set = db.ReferenceProperty(StudySet)
	date_joined = db.DateTimeProperty(auto_now_add = True)
	lastLoginTime = db.DateTimeProperty(auto_now=True)
	displayTagColumn = db.StringProperty(default = 'N')
	orientation = db.StringProperty(default = 'side-by-side')
	allowAdvertising = db.StringProperty(default = 'Y')
	gender = db.StringProperty(default = 'Male')
	birthYear = db.StringProperty(default = '1970')
	pluginPassword = db.StringProperty()

class FeedbackForAstoundLearning(db.Model):
	class Meta:
		db_table = 'FeedbackForAstoundLearning'
	user = db.ReferenceProperty(CustomUser)
	feedback = db.TextProperty()
	timestamp = db.DateTimeProperty(auto_now=True)
